using System;
using System.Threading;
using System.Windows.Forms;

namespace JK.EventRouter.Threading
{
    ///<summary>from http://msdn.microsoft.com/msdnmag/issues/07/05/CLRInsideOut/</summary>
    public class CountdownLatch
    {
        private int m_remain;
        private EventWaitHandle m_event;

        ///<summary></summary>
        public CountdownLatch(int count)
        {
            m_remain = count;
            m_event = new ManualResetEvent(false);
        }
        ///<summary></summary>
        public void Signal()
        {
            // The last thread to signal also sets the event.
            if (Interlocked.Decrement(ref m_remain) == 0)
                m_event.Set();
        }

        ///<summary></summary>
        public bool Wait()
        {
            return Wait(int.MaxValue);
        }

        ///<summary></summary>
        public bool Wait(int timeout)
        {
            if (Thread.CurrentThread.GetApartmentState() == ApartmentState.STA)
            {
                return STAWait(timeout);
            }
            else
            {
                return NormalWait(timeout);
            }
        }

        ///<summary>STAWait is used to keep the message pump running on a STA thread</summary>
        private bool STAWait(int timeout)
        {
            DateTime expireTime = DateTime.Now.AddMilliseconds(timeout);

            while(m_event.WaitOne(1, false) == false)
            {
                Application.DoEvents();  //pump for COM messages
                if (timeout >= 0 && DateTime.Now > expireTime) 
                {
                    return false; 
                }
            }

            return true;

        }

        private bool NormalWait(int timeout)
        {
            return m_event.WaitOne(timeout, false);
        }
    }
}